using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using GameEngine;
using System.Collections;

namespace DreamScape
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {

        #region Declarations

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        enum GameState {Menu, InGame, Inventory, GameOver, CutScene};
        enum RoomName { Cliff, House, Cave};

        RoomName _currentRoom = RoomName.Cliff;
        
        GameState currentGameState = GameState.InGame;

        List<Room> World { get; set; }

        RenderTarget2D mTrackRender;

        DragAbleObject test;

        PointerAvatar pointer;

        PlayerObject playerObject;
        Vector2 prePos;
        

        #endregion

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            World = new List<Room>();
            Content.RootDirectory = "Content";
        }

        #region Initialize and LoadContent

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            Functions.SetDisplayMode(ref graphics,1280, 720, false);
            World = new List<Room>();
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            Rectangle screenBounds = new Rectangle(0, 0, this.Window.ClientBounds.Width, this.Window.ClientBounds.Height);

            // Create a new SpriteBatch, which can be used to draw textures.
           

            spriteBatch = new SpriteBatch(GraphicsDevice);

            AddRoom(RoomName.Cliff, 
                new Room(
                    Functions.TextureContentLoader(Content, @"Texture\RoomFloors\Cliff"),
                    Functions.TextureContentLoader(Content, @"Texture\RoomFloors\Cliff_Collision"),
                    Vector2.Zero) { Active = true });
            
            AddBackgroundLayer(RoomName.Cliff, new DecorationLayer(Content.Load<Texture2D>(@"Texture\Backgrounds\Clouds"), Vector2.Zero, true, false, new Vector2(2, 2), new Vector2(-10, 0), screenBounds, 1));
            playerObject = new PlayerObject(Functions.TextureContentLoader(Content, @"Texture\Player\Jack01"));
            playerObject.WorldLocation = new Vector2(500, 800);

            test = new DragAbleObject(Functions.TextureContentLoader(Content, @"Texture\FileNotFound"), new Vector2(500, 800));
            Camera.Initilize(playerObject.WorldLocation, CurrentRoom.RoomBoundary, screenBounds);
            

            //camera = new Camera(new Viewport(screenBounds), CurrentRoom.RoomBoundary.Width, CurrentRoom.RoomBoundary.Height);
            pointer = new PointerAvatar(Functions.TextureContentLoader(Content, @"Texture\Pointer"));

            prePos = playerObject.WorldLocation;

            
            //Setup the render targets to be used in determining if the car is on the track
            mTrackRender = new RenderTarget2D(graphics.GraphicsDevice, playerObject.CollisionRectangle.Width + 50, playerObject.CollisionRectangle.Height + 50, false, SurfaceFormat.Color, DepthFormat.None);

        }

        #endregion

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        #region GameLoop

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {


           

            MousePointer.Position = pointer.Position;
            Camera.Position = playerObject.WorldLocation;
            
            Functions.ODS("Pointer: " + pointer.Position.ToString());
            Functions.ODS("PointerCollisionBox: " + MousePointer.Position.ToString());
            Functions.ODS("CollisionBox: " + test.WorldLocation.ToString());

            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();
           
            switch (currentGameState) { 
                case GameState.Menu:
                    break;

                case GameState.InGame:

                    prePos = playerObject.WorldLocation;
                    playerObject.Update(gameTime);
                    test.Update(gameTime);
                   

                    if (Functions.CollisionOccurred(GraphicsDevice, mTrackRender, spriteBatch, playerObject.CollisionRectangle, CurrentRoom.Floor.CollisionTexture))
                    {
                        playerObject.WorldLocation = prePos;
                    }
                    else
                        prePos = playerObject.WorldLocation;
                   
                    Camera.Position = playerObject.WorldLocation;
                    
                    break;

                case GameState.Inventory:

                    break;

                case GameState.GameOver:
                    break;

                case GameState.CutScene:
                    break;
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
           
            spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, Camera.GetViewMatrix());
                CurrentRoom.Draw(spriteBatch);
                playerObject.Draw(spriteBatch);
                test.Draw(spriteBatch);
                pointer.Draw(spriteBatch);
                
            spriteBatch.End();

            base.Draw(gameTime);
        }

        #endregion

        #region World Tools
        /// <summary>
        /// Add a room to target room list
        /// </summary>
        /// <param name="world">target List of rooms</param>
        /// <param name="roomIndex">Room index position in list</param>
        /// <param name="room">Room object that is inserted to room list</param>
        private void AddRoom(RoomName roomName, Room room)
        {
            if (room != null)
                World.Insert((int)roomName, room);
        }

        /// <summary>
        /// Add a decoration layer in the background of target room
        /// </summary>
        /// <param name="world">target List of rooms</param>
        /// <param name="roomIndex">Room index position in list</param>
        /// <param name="layer">Decorationlayer object that is inserted to target room</param>
        private void AddBackgroundLayer(RoomName roomName, DecorationLayer layer)
        {
            World.ElementAt((int)roomName).addBackgroundLayer(layer);
        }

        /// <summary>
        /// Add a decoration layer in the foreground of target room
        /// </summary>
        /// <param name="world">target List of rooms</param>
        /// <param name="roomIndex">Room index position in list</param>
        /// <param name="layer">Decorationlayer object that is inserted to target room</param>
        private void AddForegroundLayer(RoomName roomName, DecorationLayer layer)
        {
            World.ElementAt((int)roomName).addForegroundLayer(layer);
        }

        /// <summary>
        /// Returns the room on target index in room list
        /// </summary>
        /// <param name="world">target List of rooms</param>
        /// <param name="roomIndex">Room index position in list</param>
        /// <returns>the room on target index</returns>
        private Room GetTargetRoom(RoomName roomName) 
        {
            return World.ElementAt((int)roomName);
        }

        private Room CurrentRoom 
        {
            get { return GetTargetRoom(_currentRoom); }
        }

        private RoomName SetCurrentRoom {
            set { _currentRoom = value; }
        }

        #endregion


    }
}
